import chalk from "chalk";
import Cp from "child_process";
import chokidar from "chokidar";
// import Electron from "electron";
import { build as ElectronBuilder } from "electron-builder";
import { ElectronHmr } from "electron-hmr";
import { build as Esbuild } from "esbuild";
import Fs from "fs-extra";
import inquirer from "inquirer";
import klawSync from "klaw-sync";
import nodemon from "nodemon";
import Path from "path";
import { __projectDir } from "../node";
import { server as EMirrorServer } from "./electron-mirror-server";
import { default as options } from "./my-ts-configs";

/**
 * 运行某个命令，并实时输出stdout
 * @param {string} command
 * @returns error: Cp.ExecException, stdout: string, stderr: string
 */
export function exec(command: string): Promise<null | any> {
  return new Promise((resolve) =>
    // @ts-ignore
    Cp.exec(command, resolve).stdout.pipe(process.stdout)
  );
}

/**
 * 将backend中的文件变为到backend.js中的同样路径
 * @param path
 * @returns
 */
export function toNewPath(path: string): string {
  return path
    .replace(/[\\/]backend[\\/]/, "/backend.js/")
    .replace(/[\\/]shared[\\/]/, "/shared.js/")
    .replace(/\.ts$/, ".js");
}

/**
 * 将内容中的~ 和 ~/shared 和 @ 转化为相对路径
 * @param path 文件路径
 * @param content 文件内容
 * @returns 修改后的文件内容
 */
export function fixAliasProblems(path: string, content: string) {
  /** @/* 之后变为的相对路径 */
  let _at = Path.relative(
    Path.dirname(path),
    Path.resolve(__projectDir, "backend.js")
  ).replace(/\\/, "/");
  /** ~/shared 之后变为的相对路径 */
  let _shared = Path.relative(
    Path.dirname(path),
    Path.resolve(__projectDir, "shared.js")
  ).replace(/\\/g, "/");
  /** ~/* 之后变为的相对路径 */
  let _flash = Path.relative(Path.dirname(path), __projectDir).replace(
    /\\/g,
    "/"
  );
  // 如果后面没有/，加上/
  if (!/\/$/.test(_at)) {
    _at = _at + "/";
    if (_at === "/") _at = "./";
  }
  if (!/\/$/.test(_shared)) {
    _shared = _shared + "/";
    if (_shared === "/") _shared = "./";
  }
  if (!/\/$/.test(_flash)) {
    _flash = _flash + "/";
    if (_flash === "/") _flash = "./";
  }
  return content
    .replace(/"@\//g, `"${_at}`)
    .replace(/'@\//g, `'${_at}`)
    .replace(/"~\/shared/g, `"${_shared}`)
    .replace(/'~\/shared/g, `'${_shared}`)
    .replace(/"~\//g, `"${_flash}`)
    .replace(/'~\//g, `'${_flash}`);
}

/**
 * 使用esbuild编译backend的文件并将编译后的文件导入到backend.js中
 * @param {string} path
 * @param {boolean} minify
 * @returns {Promise} 编译好之后
 */
export async function buildBackendFile(path: string, minify: boolean = false) {
  let newPath = toNewPath(path);
  if (/.[jt]s$/i.test(path)) {
    try {
      await Esbuild({
        entryPoints: [path],
        platform: "node",
        bundle: false,
        minify,
        outfile: newPath,
        allowOverwrite: true,
        format: "cjs",
        sourcemap: "inline",
      });
      let content = fixAliasProblems(
        newPath,
        Fs.readFileSync(newPath, { encoding: "utf-8" })
      );
      return Fs.writeFileSync(newPath, content, { encoding: "utf-8" });
    } catch (error) {
      console.log(chalk.red(error));
    }
  } else {
    // 对于其他文件，直接拷贝过去
    return await Fs.copy(path, newPath);
  }
}

/**
 * 把backend中的所有文件都编译或复制一遍
 * @param {string} minify
 * @returns
 */
export function buildAllBackend(minify = false): Promise<any>[] {
  return klawSync(Path.resolve(__projectDir, "backend"), { nodir: true }).map(
    (e) => buildBackendFile(e.path, minify)
  );
}

/**
 * 将tsconfig输出到__projectDir/tsconfig.json，并buildAllBackend
 * @param {boolean} _minify
 * @returns
 */
export async function build(_minify = false) {
  console.log(
    "--------------------------- typescript->js ... ---------------------------"
  );
  Fs.removeSync(Path.resolve(__projectDir, "backend.js"));
  // 清屏
  process.stdout.write("\x1B[2J\x1B[0f");
  await Promise.all(buildAllBackend(_minify));
  return console.log(
    chalk.bgBlue(
      "--------------------------- typescript->js √ ---------------------------"
    )
  );
}

/**
 * 更新工程根目录下的tsconfig.json
 */
export function updateTsconfig() {
  let tsConfigPath = Path.resolve(__projectDir, "tsconfig.json");
  for (let option of options) {
    Fs.outputJsonSync(tsConfigPath, option, { spaces: "\t" });
    // await exec(command);
  }
}

/**
 * 构建build前端和后端
 */
export async function nodeBuild() {
  return await Promise.all([exec("vite build --emptyOutDir"), build()]);
}

/**
 * 对backend文件夹动态更新
 * @returns {Promise}
 */
export function dev() {
  return chokidar
    .watch([
      Path.resolve(__projectDir, "backend"),
      Path.resolve(__projectDir, "shared"),
    ])
    .on("all", async (e, path) => {
      // 清屏
      process.stdout.write("\x1B[2J\x1B[0f");
      console.log(chalk.magenta(`[${e}]: ${path}`));
      if (!Fs.existsSync(path)) {
        Fs.remove(toNewPath(path));
      } else if (Fs.statSync(path).isFile()) {
        await buildBackendFile(path);
      }
    });
}

/**
 * electron开发模式
 */
export async function electronDev() {
  await dev();
  let eHmr = new ElectronHmr({
    electronBinaryPath: require("electron").toString(),
  });
  eHmr.watch({
    include: [
      Path.resolve(__projectDir, "backend.js"),
      Path.resolve(__projectDir, "shared.js"),
    ],
  });
}

/**
 * electron打包模式
 */
export async function electronBuild() {
  let options = await inquirer.prompt({
    type: "confirm",
    name: "eServer",
    message: "启动electron-server?",
  });
  options.eServer && (await EMirrorServer());
  await nodeBuild();
  await ElectronBuilder();
  process.exit(0);
}

/**
 * node开发模式
 */
export async function nodeDev() {
  await dev();
  nodemon({
    watch: ["backend.js", "shared.js"],
    ext: "ts,mjs,js,json,graphql",
    exec: "node backend.js/index.js",
    legacyWatch: true,
  });
}
